#include "brushdef.hpp"
#include <cmath>
#include <iomanip>

#define X 0
#define Y 1
#define Z 2

using namespace std;

std::string texdef_gtk(const TPlanePoints & b) {
    std::stringstream ss;
    ss << " " << b.hshift 
       << " " << b.vshift 
       << " " << b.rotation 
       << " " << b.hscale 
       << " " << b.vscale;
    return ss.str();
}

// e.g GTK Radiant or older level editors.
void brushdef_gtk(std::stringstream &x, const vector<TPlanePoints> &y) {
	if(y.size())
	{
		x << "{" << std::endl;
		for(const TPlanePoints& krPlane : y)
		{
			x << "( " << krPlane.m_A[X] << " " << krPlane.m_A[Z] << " " << krPlane.m_A[Y] << " ) ";
			x << "( " << krPlane.m_B[X] << " " << krPlane.m_B[Z] << " " << krPlane.m_B[Y] << " ) ";
			x << "( " << krPlane.m_C[X] << " " << krPlane.m_C[Z] << " " << krPlane.m_C[Y] << " ) ";
			if(krPlane.m_material.length())
			{
				x << krPlane.m_material << texdef_gtk(krPlane) << std::endl;
			}
			else
			{
				x << "common/caulk 0 0 0 0.500000 0.500000" << std::endl;
			}
		}
		x << "}" << std::endl;
	}
}

std::string texdef_net(const TPlanePoints &b) {
// Reflex's material files haven't been reversed so there isn't actually a texture file to make use of
#define REFLEX_TEXRES_PLACEHOLDER 64
#define MAX_PRECISION 6 // note: netradiant supports up to 10
    float inverse_xscale = 0;
    if (b.hscale != 0) {
        inverse_xscale = 1 / (b.hscale * REFLEX_TEXRES_PLACEHOLDER); // WIDTH
    }
    float inverse_yscale = 0;
     if (b.vscale != 0) {
        inverse_yscale = 1 / (b.vscale * -REFLEX_TEXRES_PLACEHOLDER); // HEIGHT
    }
    // a lot of reflex's values for rotation are 0.0 or 1.0
    // likely values are stored in radians.
    float c = cos(-b.rotation); 
    float s = sin(-b.rotation);

    // horizontal values for texture definition
    float h1, h2, h3;
    h1 = c * inverse_xscale;
    h2 = s * inverse_yscale;
    h3 = b.hshift / REFLEX_TEXRES_PLACEHOLDER; // WIDTH

    float v1, v2, v3;
    v1 = -s * inverse_xscale;
    v2 = c * inverse_yscale;
    v3 = -b.vshift / -REFLEX_TEXRES_PLACEHOLDER; // HEIGHT

    std::stringstream ss;
    ss << std::fixed << std::setprecision(MAX_PRECISION) 
       << "( " << h1 << ' ' << h2 << ' ' << h3 << " ) "
       << "( " << v1 << ' ' << v2 << ' ' << v3 << " )";
   return ss.str();
}

// e.g Net Radiant and anything else based on it.
void brushdef_net(std::stringstream &x, const vector<TPlanePoints> &y) {
    if (y.size()) {
        x << "{" << std::endl;
        x << "brushDef" << std::endl;
        x << "{" << std::endl;
        for (const TPlanePoints& z : y) {
            x << "( " << z.m_A[X] << " " << z.m_A[Z] << " " << z.m_A[Y] << " ) ";
            x << "( " << z.m_B[X] << " " << z.m_B[Z] << " " << z.m_B[Y] << " ) ";
            x << "( " << z.m_C[X] << " " << z.m_C[Z] << " " << z.m_C[Y] << " ) ";
            if (z.m_material.length()) {
                // placeholder values, for now only concerned with geometry
                x << " ( " << texdef_net(z) << " ) " << z.m_material << " 0 0 0" << std::endl;
            } else {
                x << " ( " << texdef_net(z) << " ) common/caulk 0 0 0" << std::endl;
            }
        }
        x << "}" << std::endl;
        x << "}" << std::endl;
    }
}

std::string GetBrushString(const TBrush& _krBrush,
                           void (*f) (std::stringstream &, const vector<TPlanePoints> &))
{
	std::vector<TPlanePoints> Planes = GetBrushPlanes(_krBrush);
	std::stringstream ssOutput;
    f(ssOutput, Planes);
	return(ssOutput.str());
}
